package org.moonlight.jvm.instructions;

/**
 * 字节码指令实现
 *
 * 字节码就是运行在Java虚拟机上的机器码。每一个类或者接口都会被Java编译器编译成一个class文件，类或接口的方法信息就放在class文件的method_info结构中。
 * 如果方法不是抽象的，也不是本地方法，方法的Java代码就会被编译器编译成字节码（即使方法是空的，编译器也会生成一条return语句），存放在method_info结构的Code属性中。
 * 字节码中存放编码后的Java虚拟机指令。每条指令都以一个单字节的操作码（opcode）开头。由于只使用一字节表示操作码，显而易见，Java虚拟机最多只能支持256（2^8）条指令。
 * 到第八版为止，Java虚拟机规范已经定义了205条指令，操作码分别是0（0x00）到202（0xCA）、254（0xFE）和255（0xFF）。为了方便记忆，每个操作码又有独自的助记符。
 * Java虚拟机规范把已经定义的205条指令按用途分成了11类，分别是：
 *   1. 常量（constants）指令  {@link org.moonlight.jvm.instructions.constants}
 *      常量指令把常量推入操作数栈顶。常量可以来自三个地方：隐含在操作码里、操作数、运行时常量池。常量指令共有21条。
 *   2. 加载（loads）指令  {@link org.moonlight.jvm.instructions.loads}
 *      加载指令从局部变量表获取变量，然后推入操作数栈顶。加载指令共33条，按照所操作变量的类型可以分为6类：
 *         a. aload系列指令操作引用类型变量
 *         b. dload系列操作double类型变量
 *         c. fload系列操作float变量
 *         d. iload系列操作int变量
 *         e. lload系列操作long变量
 *         f. xaload操作数组
 *   3. 存储（stores）指令  {@link org.moonlight.jvm.instructions.stores}
 *      和加载指令刚好相反，存储指令把变量从操作数栈顶弹出，然后存入局部变量表。和加载指令一样，存储指令也可以分为6类:
 *         a. astore系列指令操作引用类型变量
 *         b. dstore系列操作double类型变量
 *         c. fstore系列操作float变量
 *         d. istore系列操作int变量
 *         e. lstore系列操作long变量
 *         f. xastore操作数组
 *   4. 栈（stack）指令  {@link org.moonlight.jvm.instructions.stack}
 *      栈指令直接对操作数栈进行操作，共9条：pop和pop2指令将栈顶变量弹出，dup系列指令复制栈顶变量，swap指令交换栈顶的两个变量。
 *      和其他类型的指令不同，栈指令并不关心变量类型。
 *   5. 数学（math）指令  {@link org.moonlight.jvm.instructions.math}
 *      数学指令大致对应Java语言中的加、减、乘、除等数学运算符。数学指令包括算术指令、位移指令和布尔运算指令等，共37条
 *   6. 转换（conversions）指令  {@link org.moonlight.jvm.instructions.conversions}
 *      类型转换指令大致对应Java语言中的基本类型强制转换操作, 类型转换指令有共15条。引用类型转换对应的是checkcast指令。
 *      被转换变量的类型，类型转换指令可以分为3种：
 *         1. i2x系列指令把int变量强制转换成其他类型
 *         2. 12x系列指令把long变量强制转换成其他类型
 *         3. f2x系列指令把float变量强制转换成其他类型
 *         4. d2x系列指令把double变量强制转换成其他类型
 *   7. 比较（comparisons）指令  {@link org.moonlight.jvm.instructions.comparisons}
 *      比较指令可以分为两类：一类将比较结果推入操作数栈顶，一类根据比较结果跳转。比较指令是编译器实现if-else、for、while等语句的基石，共有19条。
 *   8. 控制（control）指令  {@link org.moonlight.jvm.instructions.control}
 *      控制指令共有11条。jsr和 ret指令在Java 6之前用于实现finally子句，从Java 6开始，Oracle的Java编译器已经不再使用这两条指令了，本工程不讨论这两条指令。
 *      return系列指令有6条，用于从方法调用中返回。
 *      剩下的还有 goto、tableswitch和lookupswitch 三条指令
 *   9. 引用（references）指令  {@link org.moonlight.jvm.instructions.references}
 *      public class MyObject {
 *         public static int staticVar;
 *         public int instanceVar;
 *
 *         public static void main(String[] args) {
 *            int x = 32768; // ldc
 *            MyObject myObj = new MyObject(); // new
 *            MyObject.staticVar = x; // putstatic
 *            x = MyObject.staticVar; // getstatic
 *            myObj.instanceVar = x; // putfield
 *            x = myObj.instanceVar; // getfield
 *            Object obj = myObj;
 *            if (obj instanceof MyObject) { // instanceof
 *               myObj = (MyObject) obj; // checkcast
 *            }
 *         }
 *      }
 *   10. 扩展（extended）指令  {@link org.moonlight.jvm.instructions.extended}
 *      扩展指令共有6条。和jsr指令一样，不讨论jsr_w指令
 *   11. 保留（reserved）指令  {@link org.moonlight.jvm.instructions.reserved}
 *
 *
 * Java虚拟机解释器的大致逻辑是一个循环，每次循环都包含三个部分:
 *   1. 计算 pc
 *   2. 指令解码
 *   3. 指令执行
 * 在本项目的具体实现中:
 *   1. 把指令抽象成接口 {@link org.moonlight.jvm.instructions.base.Instruction}
 *   2. 解码、执行逻辑写在具体的指令实现中
 *
 * 从调用的角度来看，方法可以分为两类:
 *   1. 静态方法（或者类方法）
 *   2. 实例方法。
 * 静态方法通过类来调用，实例方法则通过对象引用来调用。
 * 静态方法是静态绑定的，也就是说，最终调用的是哪个方法在编译期就已经确定。实例方法则支持动态绑定，最终要调用哪个方法可能要推迟到运行期才能知道
 * 从实现的角度来看，方法可以分为三类:
 *   1. 没有实现（也就是抽象方法）
 *   2. 用Java语言（或者JVM上的其他语言，如Groovy和Scala等）实现
 *   3. 用本地语言（如C或者C++）实现
 * 静态方法和抽象方法是互斥的。在Java 8之前，接口只能包含抽象方法。为了实现Lambda表达式，Java 8放宽了这一限制，在接口中也可以定义静态方法和默认方法。
 * 在Java 7之前，Java虚拟机规范一共提供了4条方法调用指令:
 *   1. invokestatic指令用来调用静态方法
 *   2. invokespecial指令用来调用无须动态绑定的实例方法，包括构造函数、私有方法和通过super关键字调用的超类方法
 *   剩下的情况则属于动态绑定:
 *   3. invokeinterface指令，如果是针对接口类型的引用调用方法
 *   4. invokevirtual指令，如果不是针对接口类型的引用调用方法
 * 为了更好地支持动态类型语言，Java 7又增加了一条方法调用指令invokedynamic
 *
 * 方法调用执行过程:
 *   首先，方法调用指令需要n+1个操作数，其中第1个操作数是uint16索引，在字节码中紧跟在指令操作码的后面。
 *   通过这个索引，可以从当前类的运行时常量池中找到一个方法符号引用，解析这个符号引用就可以得到一个方法。
 *   注意，这个方法并不一定就是最终要调用的那个方法，所以可能还需要一个查找过程才能找到最终要调用的方法。
 *   剩下的n个操作数是要传递给被调用方法的参数，从操作数栈中弹出。
 *   如果要执行的是Java方法（而非本地方法），下一步是给这个方法创建一个新的帧，并把它推到Java虚拟机栈顶。
 *   传递参数之后，新的方法就可以开始执行了。
 */